﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SurfaceCardsBoardLibrary.Interfaces;
using System.Windows.Media;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Surface.Presentation.Controls;
using System.Media;
using SurfaceCardsBoardLibrary;
using SurfaceCardsGameKit.Games.BasicGame;
using Microsoft.Surface.Presentation;
using SurfaceCardsGameKit.Dispositions;

namespace SurfaceCardsGameKit
{
    public class Card : DependencyObject, ICard, INotifyPropertyChanged
    {
        #region Contants

        private const double ManipulationDeltaToRemoveCardFromDeck = 10;

        #endregion

        #region ICard Members
        private bool _isFront;
        private bool _isSelected;
        private bool _canTurn;
        private bool _canTurnByUser;
        private bool _canRotate;
        private bool _canScale;
        private bool _canMove;
        private ICardContainer _parent;
        private ICardContainer _lastParent;
        object _cardValue;
        private Board _board;
        private bool _canMergeByUser;

        public ICardContainer Parent
        {
            get
            {
                return this._parent;
            }
            set
            {
                if (this._parent != value)
                {
                    this._parent = value;
                    this.OnPropertyChanged("Parent");
                }
            }
        }

        public bool IsFront
        {
            get
            {
                return _isFront;
            }
            set
            {
                if (_isFront != value)
                {
                    this._isFront = value;
                    this.OnPropertyChanged("IsFront");
                }
            }
        }

        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (_isSelected != value)
                {
                    _isSelected = value;
                    OnPropertyChanged("IsSelected");
                }
            }
        }

        public bool CanTurn
        {
            get
            {
                return _canTurn;
            }
            set
            {
                if (_canTurn != value)
                {
                    this._canTurn = value;
                    this.OnPropertyChanged("CanTurn");
                }
            }
        }

        public bool CanTurnByUser
        {
            get
            {
                return _canTurnByUser;
            }
            set
            {
                if (_canTurnByUser != value)
                {
                    this._canTurnByUser = value;
                    this.OnPropertyChanged("CanTurnByUser");
                }
            }
        }

        public bool CanRotate
        {
            get
            {
                return _canRotate;
            }
            set
            {
                if (_canRotate != value)
                {
                    this._canRotate = value;
                    this.OnPropertyChanged("CanRotate");
                }
            }
        }
        public bool CanScale
        {
            get
            {
                return _canScale;
            }
            set
            {
                if (_canScale != value)
                {
                    this._canScale = value;
                    this.OnPropertyChanged("CanScale");
                }
            }
        }
        public bool CanMove
        {
            get
            {
                return _canMove;
            }
            set
            {
                if (_canMove != value)
                {
                    this._canMove = value;
                    this.OnPropertyChanged("CanMove");
                }
            }
        }

        public object CardValue
        {
            get { return _cardValue; }
            set
            {
                if (_cardValue != value)
                {
                    _cardValue = value;
                    OnPropertyChanged("CardValue");
                }
            }
        }

        public bool CanMergeByUser
        {
            get { return _canMergeByUser; }
            set
            {
                if (_canMergeByUser != value)
                {
                    _canMergeByUser = value;
                    OnPropertyChanged("CanMergeByUser");
                }
            }
        }

        public ICardContainer LastParent
        {
            get { return _lastParent; }
            set
            {
                if (_lastParent != value)
                {
                    _lastParent = value;
                    OnPropertyChanged("LastParent");
                }
            }
        }

        public Board Board
        {
            get { return _board; }
            set
            {
                if (_board != value)
                {
                    var oldSVI = ScatterViewItem;
                    _board = value;
                    var newSVI = ScatterViewItem;

                    OnScatterViewItemChanged(oldSVI, newSVI);
                }
            }
        }

        public void Turn()
        {
            Turn(FlipRotateDirection, FlipDuration, IsFlipSoundEnabled);
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, e);
            }
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #region Constructor
        public Card()
        {
            Initialize();
        }

        public Card(ICardContainer parent)
            : this()
        {
            this._parent = parent;
            Initialize();
        }

        private void Initialize()
        {
            IsFlipSoundEnabled = false;
            FlipSoundSource = @"Resources\carte.wav";
            IsBookEnabled = true;
            FlipDuration = 550;
            CanRotate = true;
            CanScale = true;
            CanMove = true;
            CanTurn = true;
            CanTurnByUser = true;
            CanMergeByUser = true;
        }
        #endregion

        #region IManipulableObject Members

        private double _defaultHeight;
        private double _defaultWidth;

        public ScatterViewItem ScatterViewItem
        {
            get
            {
                if (Board == null)
                    return null;

                return Board.ScatterView.ItemContainerGenerator.ContainerFromItem(this) as ScatterViewItem;
            }
        }

        public ScatterView ScatterView
        {
            get
            {
                return Board == null ? null : Board.ScatterView;
            }
        }

        public double Orientation
        {
            get { return (double)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Orientation.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation", typeof(double), typeof(Card), new UIPropertyMetadata((double)0));

        public Point Center
        {
            get { return (Point)GetValue(CenterProperty); }
            set { SetValue(CenterProperty, value); }
        }
        public static readonly DependencyProperty CenterProperty =
            DependencyProperty.Register("Center", typeof(Point), typeof(Card), new UIPropertyMetadata(new Point(double.NaN, double.NaN)));

        public Point ActualCenter
        {
            get { return (Point)base.GetValue(ActualCenterProperty); }
            set { base.SetValue(ActualCenterPropertyKey, value); }
        }
        private static readonly DependencyPropertyKey ActualCenterPropertyKey =
            DependencyProperty.RegisterReadOnly("ActualCenter", typeof(Point), typeof(Card),
            new FrameworkPropertyMetadata(new Point(double.NaN, double.NaN)));
        public static readonly DependencyProperty ActualCenterProperty = ActualCenterPropertyKey.DependencyProperty;

        public double Width
        {
            get { return (double)GetValue(WidthProperty); }
            set { SetValue(WidthProperty, value); }
        }
        public static readonly DependencyProperty WidthProperty =
            DependencyProperty.Register("Width", typeof(double), typeof(Card), new UIPropertyMetadata(0.0));

        public double Height
        {
            get { return (double)GetValue(HeightProperty); }
            set { SetValue(HeightProperty, value); }
        }
        public static readonly DependencyProperty HeightProperty =
            DependencyProperty.Register("Height", typeof(double), typeof(Card), new UIPropertyMetadata(0.0));

        private Visibility _visibility;
        public Visibility Visibility
        {
            get { return _visibility; }
            set
            {
                if (_visibility != value)
                {
                    _visibility = value;
                    OnPropertyChanged("Visibility");
                }
            }
        }


        public double DefaultHeight
        {
            get { return _defaultHeight; }
            set
            {
                _defaultHeight = value;
                Height = _defaultHeight;
            }
        }

        public double DefaultWidth
        {
            get { return _defaultWidth; }
            set
            {
                _defaultWidth = value;
                Width = _defaultWidth;
            }
        }

        Point _lastCenterBeforeContactsUp;
        public Point LastCenterBeforeContactsUp
        {
            get { return _lastCenterBeforeContactsUp; }
            set
            {
                if (_lastCenterBeforeContactsUp != value)
                {
                    _lastCenterBeforeContactsUp = value;
                    OnPropertyChanged("LastCenterBeforeContactsUp");
                }
            }
        }

        #endregion

        #region Properties

        #region Front

        public object FrontContent
        {
            get { return (object)GetValue(FrontContentProperty); }
            set { SetValue(FrontContentProperty, value); }
        }

        public DataTemplate FrontContentTemplate
        {
            get { return (DataTemplate)base.GetValue(FrontContentTemplateProperty); }
            set { base.SetValue(FrontContentTemplateProperty, value); }
        }

        public DataTemplateSelector FrontContentTemplateSelector
        {
            get { return (DataTemplateSelector)base.GetValue(FrontContentTemplateSelectorProperty); }
            set { base.SetValue(FrontContentTemplateSelectorProperty, value); }
        }

        public string FrontContentStringFormat
        {
            get { return (string)base.GetValue(FrontContentStringFormatProperty); }
            set { base.SetValue(FrontContentStringFormatProperty, value); }
        }

        public Brush FrontBackground
        {
            get { return (Brush)GetValue(FrontBackgroundProperty); }
            set { SetValue(FrontBackgroundProperty, value); }
        }

        public static readonly DependencyProperty FrontContentProperty =
            DependencyProperty.Register("FrontContent", typeof(object), typeof(Card), new UIPropertyMetadata(null));

        public static readonly DependencyProperty FrontContentTemplateProperty =
            DependencyProperty.Register("FrontContentTemplate",
            typeof(DataTemplate), typeof(Card), new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty FrontContentTemplateSelectorProperty =
            DependencyProperty.Register("FrontContentTemplateSelector",
            typeof(DataTemplateSelector), typeof(Card), new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty FrontContentStringFormatProperty =
            DependencyProperty.Register("FrontContentStringFormat", typeof(string), typeof(Card), new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty FrontBackgroundProperty =
            DependencyProperty.Register("FrontBackground", typeof(Brush), typeof(Card), new UIPropertyMetadata(null));

        #endregion

        #region Back

        public object BackContent
        {
            get { return (object)GetValue(BackContentProperty); }
            set { SetValue(BackContentProperty, value); }
        }

        public DataTemplate BackContentTemplate
        {
            get { return (DataTemplate)base.GetValue(BackContentTemplateProperty); }
            set { base.SetValue(BackContentTemplateProperty, value); }
        }

        public DataTemplateSelector BackContentTemplateSelector
        {
            get { return (DataTemplateSelector)base.GetValue(BackContentTemplateSelectorProperty); }
            set { base.SetValue(BackContentTemplateSelectorProperty, value); }
        }

        public string BackContentStringFormat
        {
            get { return (string)base.GetValue(BackContentStringFormatProperty); }
            set { base.SetValue(BackContentStringFormatProperty, value); }
        }

        public Brush BackBackground
        {
            get { return (Brush)GetValue(BackBackgroundProperty); }
            set { SetValue(BackBackgroundProperty, value); }
        }

        public static readonly DependencyProperty BackContentProperty =
            DependencyProperty.Register("BackContent", typeof(object), typeof(Card), new UIPropertyMetadata(null));

        public static readonly DependencyProperty BackContentTemplateProperty =
            DependencyProperty.Register("BackContentTemplate",
            typeof(DataTemplate), typeof(Card), new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty BackContentTemplateSelectorProperty =
            DependencyProperty.Register("BackContentTemplateSelector",
            typeof(DataTemplateSelector), typeof(Card), new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty BackContentStringFormatProperty =
            DependencyProperty.Register("BackContentStringFormat", typeof(string), typeof(Card), new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty BackBackgroundProperty =
            DependencyProperty.Register("BackBackground", typeof(Brush), typeof(Card), new UIPropertyMetadata(null));

        #endregion

        public Visibility BookPageVisibility
        {
            get { return (Visibility)GetValue(BookPageVisibilityProperty); }
            set { SetValue(BookPageVisibilityProperty, value); }
        }

        public static readonly DependencyProperty BookPageVisibilityProperty =
            DependencyProperty.Register("BookPageVisibility", typeof(Visibility), typeof(Card), new UIPropertyMetadata(Visibility.Visible));

        #region Book pages corners

        public bool IsTopLeftCornerEnabled
        {
            get { return (bool)GetValue(IsTopLeftCornerEnabledProperty); }
            set { SetValue(IsTopLeftCornerEnabledProperty, value); }
        }
        public static readonly DependencyProperty IsTopLeftCornerEnabledProperty =
            DependencyProperty.Register("IsTopLeftCornerEnabled", typeof(bool), typeof(Card), new UIPropertyMetadata(false));

        public bool IsBottomLeftCornerEnabled
        {
            get { return (bool)GetValue(IsBottomLeftCornerEnabledProperty); }
            set { SetValue(IsBottomLeftCornerEnabledProperty, value); }
        }
        public static readonly DependencyProperty IsBottomLeftCornerEnabledProperty =
            DependencyProperty.Register("IsBottomLeftCornerEnabled", typeof(bool), typeof(Card), new UIPropertyMetadata(false));

        public bool IsTopRightCornerEnabled
        {
            get { return (bool)GetValue(IsTopRightCornerEnabledProperty); }
            set { SetValue(IsTopRightCornerEnabledProperty, value); }
        }
        public static readonly DependencyProperty IsTopRightCornerEnabledProperty =
            DependencyProperty.Register("IsTopRightCornerEnabled", typeof(bool), typeof(Card), new UIPropertyMetadata(false));

        public bool IsBottomRightCornerEnabled
        {
            get { return (bool)GetValue(IsBottomRightCornerEnabledProperty); }
            set { SetValue(IsBottomRightCornerEnabledProperty, value); }
        }
        public static readonly DependencyProperty IsBottomRightCornerEnabledProperty =
            DependencyProperty.Register("IsBottomRightCornerEnabled", typeof(bool), typeof(Card), new UIPropertyMetadata(false));

        #endregion

        private bool _isBlocked;
        public bool IsBlocked
        {
            get { return this._isBlocked; }
            set
            {
                if (this._isBlocked != value)
                {
                    this._isBlocked = value;
                    this.OnPropertyChanged("IsBlocked");
                }
            }
        }

        private bool _isBookEnabled;
        public bool IsBookEnabled
        {
            get { return this._isBookEnabled; }
            set
            {
                if (this._isBookEnabled != value)
                {
                    this._isBookEnabled = value;
                    this.OnPropertyChanged("IsBookEnabled");
                }
            }
        }

        bool _isFlipSoundEnabled;
        public bool IsFlipSoundEnabled
        {
            get { return _isFlipSoundEnabled; }
            set
            {
                if (_isFlipSoundEnabled != value)
                {
                    _isFlipSoundEnabled = value;
                    OnPropertyChanged("IsFlipSoundEnabled");
                }
            }
        }

        string _flipSoundSource;
        public string FlipSoundSource
        {
            get { return _flipSoundSource; }
            set
            {
                if (_flipSoundSource != value)
                {
                    _flipSoundSource = value;
                    OnPropertyChanged("FlipSoundSource");
                }
            }
        }

        int _flipDuration;
        /// <summary>
        /// The flip animation duration in milliseconds
        /// </summary>
        public int FlipDuration
        {
            get { return _flipDuration; }
            set
            {
                if (_flipDuration != value)
                {
                    _flipDuration = value;
                    OnPropertyChanged("FlipDuration");
                }
            }
        }

        RotateDirection _flipRotateDirection;
        public RotateDirection FlipRotateDirection
        {
            get { return _flipRotateDirection; }
            set
            {
                if (_flipRotateDirection != value)
                {
                    _flipRotateDirection = value;
                    OnPropertyChanged("FlipRotateDirection");
                }
            }
        }


        bool _useBackgroundOnlyForFlip;
        /// <summary>
        /// Setting it to true avoid doing a "screenshot" of the control before flipping it
        /// </summary>
        public bool UseBackgroundOnlyForFlip
        {
            get { return _useBackgroundOnlyForFlip; }
            set
            {
                if (_useBackgroundOnlyForFlip != value)
                {
                    _useBackgroundOnlyForFlip = value;
                    OnPropertyChanged("UseBackgroundOnlyForFlip");
                }
            }
        }

        public BasicCardValue BasicCardValue
        {
            get { return (BasicCardValue)CardValue; }
        }

        public Deck Deck
        {
            get
            {
                return Parent == null ? null : Parent as Deck;
            }
        }

        DispositionOffset? _heapDispositionOffset;
        public DispositionOffset? HeapDispositionOffset
        {
            get { return _heapDispositionOffset; }
            set
            {
                _heapDispositionOffset = value;
                OnPropertyChanged("HeapDispositionOffset");
            }
        }

        public Func<RotateDirection, Duration, bool, bool> BeginFlipAnimation { get; set; }
        public Action<Card, ContactEventArgs> CheckCaptureContact { get; set; }

        #region Shadow

        public Thickness ShadowMargin
        {
            get { return (Thickness)GetValue(ShadowMarginProperty); }
            set { SetValue(ShadowMarginProperty, value); }
        }
        public static readonly DependencyProperty ShadowMarginProperty =
            DependencyProperty.Register("ShadowMargin", typeof(Thickness), typeof(Card), new UIPropertyMetadata(new Thickness(-5)));

        public CornerRadius ShadowCornerRadius
        {
            get { return (CornerRadius)GetValue(ShadowCornerRadiusProperty); }
            set { SetValue(ShadowCornerRadiusProperty, value); }
        }
        public static readonly DependencyProperty ShadowCornerRadiusProperty =
            DependencyProperty.Register("ShadowCornerRadius", typeof(CornerRadius), typeof(Card), new UIPropertyMetadata(new CornerRadius(5)));

        public double ShadowOpacity
        {
            get { return (double)GetValue(ShadowOpacityProperty); }
            set { SetValue(ShadowOpacityProperty, value); }
        }
        public static readonly DependencyProperty ShadowOpacityProperty =
            DependencyProperty.Register("ShadowOpacity", typeof(double), typeof(Card), new UIPropertyMetadata(.75));

        public Visibility ShadowVisibility
        {
            get { return (Visibility)GetValue(ShadowVisibilityProperty); }
            set { SetValue(ShadowVisibilityProperty, value); }
        }
        public static readonly DependencyProperty ShadowVisibilityProperty =
            DependencyProperty.Register("ShadowVisibility", typeof(Visibility), typeof(Card), new UIPropertyMetadata(Visibility.Collapsed));

        public Color ShadowColor
        {
            get { return (Color)GetValue(ShadowColorProperty); }
            set { SetValue(ShadowColorProperty, value); }
        }
        public static readonly DependencyProperty ShadowColorProperty =
            DependencyProperty.Register("ShadowColor", typeof(Color), typeof(Card), new UIPropertyMetadata(Colors.Black));

        #endregion

        #endregion

        public void Turn(RotateDirection rotate)
        {
            Turn(rotate, FlipDuration, IsFlipSoundEnabled);
        }

        public void Turn(int duration)
        {
            Turn(FlipRotateDirection, duration, IsFlipSoundEnabled);
        }

        public void Turn(bool soundenabled)
        {
            Turn(FlipRotateDirection, FlipDuration, soundenabled);
        }

        public void Turn(RotateDirection rotate, int duration, bool soundenabled)
        {
            if (!CanTurn)
                return;

            Duration dur = new Duration(TimeSpan.FromMilliseconds(duration));

            IsFront = !IsFront;

            if (BeginFlipAnimation != null)
            {
                BookPageVisibility = Visibility.Collapsed;
                var retFlip = BeginFlipAnimation(rotate, dur, UseBackgroundOnlyForFlip);

                if (!retFlip)
                    BookPageVisibility = Visibility.Visible;
            }

            // flip content && background
            var temp = FrontContent; FrontContent = BackContent; BackContent = temp;
            var temp1 = FrontContentStringFormat; FrontContentStringFormat = BackContentStringFormat; BackContentStringFormat = temp1;
            var temp2 = FrontContentTemplate; FrontContentTemplate = BackContentTemplate; BackContentTemplate = temp2;
            var temp3 = FrontContentTemplateSelector; FrontContentTemplateSelector = BackContentTemplateSelector; BackContentTemplateSelector = temp3;
            var temp4 = FrontBackground; FrontBackground = BackBackground; BackBackground = temp4;

            if (soundenabled)
            {
                SoundPlayer _sound = new SoundPlayer(FlipSoundSource);
                _sound.Play();
            }

        }

        private void OnScatterViewItemChanged(ScatterViewItem oldValue, ScatterViewItem newValue)
        {
            if (oldValue != null)
            {
                oldValue.Activated -= OnScatterViewItemActivated;
                oldValue.Deactivated -= OnScatterViewItemDeactivated;
                oldValue.ScatterManipulationStarted -= OnScatterManipulationStarted;
                oldValue.ScatterManipulationDelta -= OnScatterManipulationDelta;
                oldValue.ScatterManipulationCompleted -= OnScatterManipulationCompleted;
                oldValue.ContactTapGesture -= OnScatterViewItemTapGesture;
                oldValue.PreviewContactDown -= OnScatterViewItemTapGesturePreviewContactDown;
            }
            if (newValue != null)
            {
                newValue.Activated += OnScatterViewItemActivated;
                newValue.Deactivated += OnScatterViewItemDeactivated;
                newValue.ScatterManipulationStarted += OnScatterManipulationStarted;
                newValue.ScatterManipulationDelta += OnScatterManipulationDelta;
                newValue.ScatterManipulationCompleted += OnScatterManipulationCompleted;
                newValue.ContactTapGesture += OnScatterViewItemTapGesture;
                newValue.PreviewContactDown += OnScatterViewItemTapGesturePreviewContactDown;
            }
        }

        private void OnScatterViewItemActivated(object sender, RoutedEventArgs e)
        {
            // Set it TopMost if needed
            if (!this.ScatterViewItem.IsTopmostOnActivation)
                Activate();
        }

        public void Activate()
        {
            this.ScatterViewItem.SetRelativeZIndex(RelativeScatterViewZIndex.Topmost);
        }

        private void OnScatterViewItemDeactivated(object sender, RoutedEventArgs e)
        {
            this.ScatterViewItem.SetRelativeZIndex(RelativeScatterViewZIndex.Bottommost);

            // Recalculate Z Index for the deck
            if (Deck != null)
                Deck.UpdateDisposition(true, true);
        }

        private Point _manipulationOriginCenter;

        private void OnScatterManipulationStarted(object sender, ScatterManipulationStartedEventArgs e)
        {
            if (this.Deck != null)
            {
                if (!this.Deck.CanRemoveByUser)
                    this.ScatterViewItem.CancelManipulation();
                _manipulationOriginCenter = this.ScatterViewItem.ActualCenter;
            }
        }

        private void OnScatterManipulationDelta(object sender, ScatterManipulationDeltaEventArgs e)
        {
            if (this.Deck != null
                &&
                (Math.Abs(this.ScatterViewItem.ActualCenter.X - _manipulationOriginCenter.X) > ManipulationDeltaToRemoveCardFromDeck
                || Math.Abs(this.ScatterViewItem.ActualCenter.Y - _manipulationOriginCenter.Y) > ManipulationDeltaToRemoveCardFromDeck))
            {
                _board.RemoveCardFromDeck(this.Deck, this);
            }
        }

        private void OnScatterManipulationCompleted(object sender, ScatterManipulationCompletedEventArgs e)
        {
        }

        private void OnScatterViewItemTapGesturePreviewContactDown(object sender, ContactEventArgs e)
        {
            // check with the deck if the contact needs to be catched
            if (CheckCaptureContact != null)
                CheckCaptureContact(this, e);
        }

        private void OnScatterViewItemTapGesture(object sender, ContactEventArgs e)
        {
            if (Deck != null)
                Deck.ToggleIsSelected(this);
        }

        public Action<ContactEventArgs> OpacityMaskPoint { get; set; }


        public void SetOpacityMaskPoint(ContactEventArgs e)
        {
            if (OpacityMaskPoint != null)
            {
                OpacityMaskPoint(e);
            }
        }

        public Action CancelOpacityMask { get; set; }


        public void SetCancelOpacityMask()
        {
            if (CancelOpacityMask != null)
            {
                CancelOpacityMask();
            }
        }
    }
}
